package com.budwk.app.sqfwDoc.codeGenerator.impl;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.jim.SqfwWebSocket;
import com.budwk.app.jim.WebSocketMsgDTO;
import com.budwk.app.sqfw.dto.SqfwCompleteFlowUpdateDTO;
import com.budwk.app.sqfw.enums.SqfwFormSummaryFieldEnum;
import com.budwk.app.sqfw.models.Sqfw_completeflow;
import com.budwk.app.sqfw.models.Sqfw_formSummary_new;
import com.budwk.app.sqfw.services.SqfwCompleteflowService;
import com.budwk.app.sqfw.services.SqfwFormsummaryNewService;
import com.budwk.app.sqfw.services.SqfwWorkorderService;
import com.budwk.app.sqfw.services.datamange.DataMangeCommunityService;
import com.budwk.app.sqfw.vo.GenerateWordAndPdfUrlVO;
import com.budwk.app.sqfwDoc.bo.PdfGenerateRes;
import com.budwk.app.sqfwDoc.codeGenerator.bo.PdfCreatedBridgeBO;
import com.budwk.app.sqfwDoc.codeGenerator.pdfGenerate.AbstractCustomerPdfCreateField;
import com.budwk.app.sqfwDoc.codeGenerator.pdfGenerate.BasePdfParamDTO;
import com.budwk.app.sqfwDoc.codeGenerator.pdfGenerate.BaseTemplatePdfGenerator;
import com.budwk.app.sqfwDoc.codeGenerator.pdfGenerate.SqfwPdfCreateCommonBO;
import com.budwk.app.sqfwDoc.dto.TemplateCodeUpdateDTO;
import com.budwk.app.sqfwV2.service.ShopsBaseInfoService;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.sys.services.SysUserService;
import com.budwk.app.util.StrUtil;
import com.budwk.app.util.ThreadLocalUtil;
import com.budwk.app.util.ThreadPoolExecutorUtil;
import com.budwk.app.util.poiUtils.nutzBeanUtils.NutzIocUtils;
import com.budwk.starter.security.utils.SecurityUtil;
import com.deepoove.poi.XWPFTemplate;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.nutz.dao.Cnd;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.trans.Trans;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.budwk.app.util.poiUtils.CommonFormOfficeUtils.wordToPdfAndUpload;

/**
 * @author ZY.Wang
 * @since 2023/12/21
 * <p>
 * 这个类的主要作用，这个类虽然是一个抽象类，但是它已经具备独立生成pdf、并且能在生成成功之后通知前端的能力
 * <p>
 * 凡是继承这个类的子类，都需要实现getCodeOfficeGenerator这个方法，在这个方法中就确定了究竟使用哪一个类来生成pdf
 */
@Slf4j
public abstract class SqfwTemplatePdfCommonService {

    ExecutorService executorService = Executors.newFixedThreadPool(20);

    @Inject
    private DataMangeCommunityService dataMangeCommunityService;

    /**
     * 使用工作流方式创建pdf
     *
     * @param userId 用户id
     * @param dto    工作流以及表单数据
     */
    public void generatePdfAndMessage(String userId, TemplateCodeUpdateDTO dto) {

        insertWordFlowAndFormSummary(userId, dto);

        Sqfw_completeflow completeFlow = dto.getCompleteFlow();
        Sqfw_formSummary_new formSummary = dto.getFormSummary();

        executorService.execute(() -> generatePdfByFlowAndForm(userId, completeFlow, formSummary, false));

    }

    /**
     * 重新生成pdf
     * 工作流id
     */
    public void reGeneratePDF(String sqfwFlowId) {
        log.info("reGeneratePDF start");
        // 1 属性注入
        SqfwCompleteflowService sqfwCompleteflowService = NutzIocUtils.getBean(SqfwCompleteflowService.class);
        SqfwFormsummaryNewService sqfwFormsummaryNewService = NutzIocUtils.getBean(SqfwFormsummaryNewService.class);

        // 2 查询工作留信息
        SqfwCompleteFlowUpdateDTO updateDTO = new SqfwCompleteFlowUpdateDTO();
        Sqfw_completeflow completeFlow = sqfwCompleteflowService.fetch(sqfwFlowId);
        BeanUtil.copyProperties(completeFlow, updateDTO);

        // 3 查询社区表信息
        Cnd cnd = Cnd.NEW();
        SqlExpressionGroup sqlExpressionGroup = cnd.where();
        sqlExpressionGroup.andEquals(Sqfw_formSummary_new::getSqfwFlowId, sqfwFlowId);
        Sqfw_formSummary_new formSummaryNews = sqfwFormsummaryNewService.fetch(cnd);

        beforeReGenPdf(formSummaryNews);

        // 4 重新生成数据
        String userId = SecurityUtil.getUserId();

        executorService.execute(() -> generatePdfByFlowAndForm(userId, updateDTO, formSummaryNews, true));

    }

    /**
     * 插入数据
     *
     * @param userId              用户id
     * @param formSummaryWithFlow 工作流
     */
    private void insertWordFlowAndFormSummary(String userId, TemplateCodeUpdateDTO formSummaryWithFlow) {
        List<String> ids = new ArrayList<String>();
        Trans.exec(() -> {
            SysUserService sysUserService = NutzIocUtils.getBean(SysUserService.class);
            SqfwCompleteflowService sqfwCompleteflowService = NutzIocUtils.getBean(SqfwCompleteflowService.class);
            SqfwWorkorderService sqfwWorkorderService = NutzIocUtils.getBean(SqfwWorkorderService.class);
            ShopsBaseInfoService shopsBaseInfoService = NutzIocUtils.getBean(ShopsBaseInfoService.class);
            SqfwFormsummaryNewService sqfwFormsummaryNewService = NutzIocUtils.getBean(SqfwFormsummaryNewService.class);

            Sys_user sysUser = sysUserService.fetch(userId);

            Sqfw_formSummary_new formSummary = formSummaryWithFlow.getFormSummary();
            // 首先现需要生成一个工作流信息
            SqfwCompleteFlowUpdateDTO completeFlow = formSummaryWithFlow.getCompleteFlow();
            if (StringUtils.isEmpty(formSummary.getType())) {
                formSummary.setType(getSummaryType());
            }
            if (ObjectUtils.isEmpty(completeFlow)) {
                completeFlow = new SqfwCompleteFlowUpdateDTO();
                completeFlow.setWorkerId(StringUtils.isNotEmpty(formSummary.getWorkOrderId()) ? formSummary.getWorkOrderId() : null);
                completeFlow.setFileType(formSummary.getType());
                completeFlow.setLatestFlowRemark("完成了文档");
                completeFlow.setTitle("完成了文档");
                completeFlow.setSqfwId(StringUtils.isNotEmpty(formSummary.getSqfwId()) ? formSummary.getSqfwId() : StrUtil.getUUID());
            }
            completeFlow.setUserId(sysUser.getId());
            completeFlow.setUserName(sysUser.getUsername());
            completeFlow.setResourcePath("creating");
            completeFlow.setCreateDate(new Date());

            beforeInsertCompleteFlow(completeFlow);
            sqfwCompleteflowService.insert(completeFlow);
            if (ObjectUtils.isEmpty(formSummaryWithFlow.getCompleteFlow())) {
                formSummaryWithFlow.setCompleteFlow(completeFlow);
            }
            afterInsertCompleteFlow(completeFlow);

            // 更新工单表中的最新工作流备注
            sqfwWorkorderService.updateLatestFlowRemarkWithCompleteFlow(completeFlow.getWorkerId(), completeFlow.getFileType(), false);


            formSummary.setParamData(formSummary.getParamData());
            formSummary.setCreateDate(new Date());
            formSummary.setCreateBy(userId);
            formSummary.setSqfwId(StringUtils.isNotEmpty(formSummary.getSqfwId()) ? formSummary.getSqfwId() :completeFlow.getSqfwId());
            formSummary.setWorkOrderId(StringUtils.isNotEmpty(formSummary.getWorkOrderId()) ? formSummary.getWorkOrderId() :completeFlow.getWorkerId());
            formSummary.setSqfwFlowId(completeFlow.getId());
            formSummary.setCreateUserName(sysUser.getUsername());
            formSummary.setCreateUserPhone(sysUser.getLoginname());
            formSummary.setUpdateBy(sysUser.getId());
            formSummary.setUpdateDate(new Date());
            formSummary.setUpdateUserName(sysUser.getUsername());
            formSummary.setUpdateUserPhone(sysUser.getLoginname());
            formSummary.setType(getSummaryType());
            formSummary.setProvince(sysUser.getProvince());
            formSummary.setCity(sysUser.getCity());
            formSummary.setCounty(sysUser.getCounty());
            formSummary.setDistrict(sysUser.getDistrict());
            formSummary.setCommunity(sysUser.getCommunity());
            if (StringUtils.isEmpty(formSummary.getCheckStatus())) {
                formSummary.setCheckStatus(SqfwFormSummaryFieldEnum.check_status_init.getCode());
            }
            formSummary.setUnitName(sysUser.getUnitName());
            formSummary.setUnitId(sysUser.getUnitId());

            if (formSummary.getFillDate() == null) {
                formSummary.setFillDate(new Date());
            }

            beforeInsertForSummary(formSummary);
            sqfwFormsummaryNewService.insert(formSummary);
            afterInsertForSummary(formSummary);
            ids.add(formSummary.getId());
        });
        // 异步解析入数据管理模块
        try {
            ExecutorService executorService = ThreadPoolExecutorUtil.getInstance();
            CompletableFuture.runAsync(() -> {
                dataMangeCommunityService.cleanDataBySummaryId(ids);
            }, executorService);
        } catch (Exception ex) {
            log.error("clean data service error! id : {}", JSONUtil.toJsonStr(ids));
        }
    }

    /**
     * 在插入工作流之前要处理的事情
     *
     * @param completeFlow 工作流
     */
    protected void beforeInsertCompleteFlow(SqfwCompleteFlowUpdateDTO completeFlow) {
    }

    /**
     * 在完成工作流之后需要完成的事情
     *
     * @param completeFlow 工作流已经存在id
     */
    protected void afterInsertCompleteFlow(SqfwCompleteFlowUpdateDTO completeFlow) {
    }

    /**
     * 在插入formSummary之前
     *
     * @param formSummary formSummary
     */
    protected void beforeInsertForSummary(Sqfw_formSummary_new formSummary) {
    }

    /**
     * 在插入formSummary之前
     *
     * @param formSummary formSummary
     */
    protected void afterInsertForSummary(Sqfw_formSummary_new formSummary) {
    }

    /**
     * 重新创建pdf之前可以自定义进行操作
     *
     * @param formSummaryNews 表单数据
     */
    protected void beforeReGenPdf(Sqfw_formSummary_new formSummaryNews) {

    }

    /**
     * 开始创建垃圾分类
     *
     * @param userId       用户id
     * @param completeFlow 工作流
     * @param formSummary  填表数据
     */
    private void generatePdfByFlowAndForm(String userId, Sqfw_completeflow completeFlow, Sqfw_formSummary_new formSummary, Boolean reCreatePdfFlag) {
        ThreadLocalUtil.setUserId(userId);
        SysUserService sysUserService = NutzIocUtils.getBean(SysUserService.class);
        SqfwCompleteflowService sqfwCompleteflowService = NutzIocUtils.getBean(SqfwCompleteflowService.class);
        SqfwWebSocket sqfwWebSocket = NutzIocUtils.getBean(SqfwWebSocket.class);

        SqfwPdfCreateCommonBO pdfCreateCommonBO = new SqfwPdfCreateCommonBO();

        // 设置一些基础的信息
        BasePdfParamDTO basePdfParamDTO = new BasePdfParamDTO();
        BeanUtil.copyProperties(formSummary, basePdfParamDTO);
        basePdfParamDTO.setCreateUserId(formSummary.getCreateBy());
        basePdfParamDTO.setFormSummaryTypeCode(getSummaryType());
        basePdfParamDTO.setCreateUserName(formSummary.getCreateUserName());
        basePdfParamDTO.setCreateUserPhone(formSummary.getCreateUserPhone());
        basePdfParamDTO.setCreateUserSigUrl(sysUserService.fetch(userId).getSignatureUrl());
        basePdfParamDTO.setSqfwId(completeFlow.getSqfwId());

        Date fillDate = formSummary.getFillDate();
        if (fillDate == null) fillDate = formSummary.getCreateDate();
        basePdfParamDTO.setFillDate(fillDate);
        basePdfParamDTO.setProvince(formSummary.getProvince());
        basePdfParamDTO.setCity(formSummary.getCity());
        basePdfParamDTO.setDistrict(formSummary.getDistrict());
        basePdfParamDTO.setCounty(formSummary.getCounty());
        basePdfParamDTO.setCommunity(formSummary.getCommunity());

        pdfCreateCommonBO.setBasePdfParamDTO(basePdfParamDTO);

        AbstractCustomerPdfCreateField pdfField = getPdfCreateField(formSummary);
        pdfCreateCommonBO.setAbstractCustomerPdfCreateField(pdfField);

        customerPopularPdfCreateParam(pdfCreateCommonBO, completeFlow, formSummary);

        try {
            List<BaseTemplatePdfGenerator> generatorList = getPdfGenerator(completeFlow, formSummary);

            List<ByteArrayOutputStream> osList = Lists.newArrayList();

            List<PdfGenerateRes> pdfGenerateResList = Lists.newArrayList();

            for (BaseTemplatePdfGenerator generator : generatorList) {
                XWPFTemplate pdfTemplate = generator.createPdf(pdfCreateCommonBO);
                // 生成pdf后的操作
                ByteArrayOutputStream pdfOS = generator.generatePDFAfter(pdfTemplate, formSummary);
                if (pdfOS == null) {
                    if (pdfTemplate == null) continue;
                    pdfOS = new ByteArrayOutputStream();
                    pdfTemplate.write(pdfOS);
                }
                if (pdfOS.size() > 0) {
                    osList.add(pdfOS);

                    PdfGenerateRes pdfGenerateRes = new PdfGenerateRes();
                    pdfGenerateRes.setGenerator(generator);
                    pdfGenerateRes.setBusinessData(formSummary);
                    pdfGenerateRes.setPdfOS(pdfOS);
                    pdfGenerateResList.add(pdfGenerateRes);
                }
            }

            // 可以根据生成文件os自定义其他步骤,和上面的generatePDFAfter有点相似
            // 但是不同的是generatePDFAfter是generator层的，而这个方法是在service层的
            getPdfGenerateResAfter(pdfGenerateResList, formSummary);

            String fileName = getDownloadFileName(completeFlow, formSummary);


            GenerateWordAndPdfUrlVO wordVO = wordToPdfAndUpload(osList, fileName, formSummary.getUnitName() + formSummary.getType());

            // 创建pdf生成之后需要向下传递的一些参数
            PdfCreatedBridgeBO pdfCreatedBridgeBO = new PdfCreatedBridgeBO();
            pdfCreatedBridgeBO.setReCreateFlag(reCreatePdfFlag);
            pdfCreatedBridgeBO.setUserId(userId);
            pdfCreatedBridgeBO.setLastCreateUrl(completeFlow.getResourcePath());
            //清空ThreadLocal中的userId
            ThreadLocalUtil.clearUserId();
            if (StringUtils.isBlank(wordVO.getPdfUrl())) {
                completeFlow.setResourcePath("error");
            } else {
                completeFlow.setResourcePath(wordVO.getPdfUrl());
                sqfwCompleteflowService.update(completeFlow);

                // 发送一个websocket通知前端
                WebSocketMsgDTO msgDTO = new WebSocketMsgDTO();
                msgDTO.setType(getPdfCreateSocketType());
                msgDTO.setMsg("PDF生成成功");
                sqfwWebSocket.sendMessage(userId, msgDTO);

                // 再次重新赋值工作流的数据
                pdfCreatedBridgeBO.setCompleteFlow(completeFlow);
                pdfCreatedBridgeBO.setFormSummaryNew(formSummary);

                afterCreatePdfSuccess(pdfCreatedBridgeBO);
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("创建PDF失败");
            completeFlow.setResourcePath("error");
            sqfwCompleteflowService.update(completeFlow);
        }
    }

    /**
     * 在创建pdf之后可以进行其他操作
     *
     * @param pdfGenerateResList pdf创建结果
     */
    protected void getPdfGenerateResAfter(List<PdfGenerateRes> pdfGenerateResList, Sqfw_formSummary_new formSummary) {

    }

    /**
     * 创建成功之后允许自定义一些功能
     */
    protected void afterCreatePdfSuccess(PdfCreatedBridgeBO pdfCreatedBridgeBO) {

    }

    protected abstract String getDownloadFileName(Sqfw_completeflow completeFlow, Sqfw_formSummary_new formSummaryNew);

    /**
     * 允许用户在生成pdf之前能够对生成pdf的材料数据进行赋值
     */
    protected abstract void customerPopularPdfCreateParam(SqfwPdfCreateCommonBO pdfCreateCommonBO, Sqfw_completeflow completeFlow, Sqfw_formSummary_new formSummary);

    /**
     * 在这个地方决定究竟使用那个pdf来生成
     *
     * @param completeFlow 工作流
     * @param formSummary  填表的数据
     * @return 选择使用的生成器
     */
    protected abstract List<BaseTemplatePdfGenerator> getPdfGenerator(Sqfw_completeflow completeFlow, Sqfw_formSummary_new formSummary);

    /**
     * 需要从formSummary中的paramData的Json数据中解析出自己所需要的类，这个类在代码生成的时候一起创建了
     *
     * @param formSummary 前端填写的表单数据
     * @return 结果
     */
    protected abstract AbstractCustomerPdfCreateField getPdfCreateField(Sqfw_formSummary_new formSummary);

    /**
     * 第五张表中的需要的类型，详情见 {@link com.budwk.app.sqfw.constants.SqfwFormSummaryConstants}
     *
     * @return 返回数据
     */
    protected abstract String getSummaryType();

    /**
     * 当pdf生成成功的时候，sock发送什么样的消息至前端
     * 参照{@link com.budwk.app.sqfw.constants.SqfwWebSocketMsgConstants}
     *
     * @return 结果
     */
    protected abstract String getPdfCreateSocketType();
}
